package emulator.assembler;

import java.io.InputStream;

import emulator.hardware.HwByte;
import emulator.hardware.nmos6502.CommandDefinition;
import emulator.hardware.nmos6502.CommandSet;
import emulator.util.MemoryPtr;

public class Assembler
{
	private SymbolTable symbols;
	private CommandSet commands;
	
	public Assembler()
	{
		symbols = new SymbolTable();
		commands = new CommandSet();
	}
	
	public InstructionList disassemble(byte[] memory, int start, int end)
	{
		InstructionList instructions = new InstructionList(symbols);
		int address = start;
		while (address <= end)
			address = disassembleOneInstruction(memory, instructions, address); 
		return instructions;
	}

	public InstructionList disassembleLines(byte[] memory, int start, int line_cnt)
	{
		InstructionList instructions = new InstructionList(symbols);
		int address = start;
		for (int i = 0; i < line_cnt && address < 0x10000; i++)
			address = disassembleOneInstruction(memory, instructions, address); 
		return instructions;
	}

	public InstructionList disassembleLines(DataDescriptionTable table, byte[] memory, int start, int line_cnt)
	{
		InstructionList instructions = new InstructionList(symbols);
		int address = start;
		for (int i = 0; i < line_cnt && address < 0x10000; i++)
		{
			DataDescription description = table.getDescriptionForAddress(address);
			address = dissambleData(memory, instructions, description);
		}
		return instructions;
	}

	private int dissambleData(byte[] memory, InstructionList instructions,
			DataDescription description) {
		int address = description.getAddress();
		if (description.isCode())
			address = disassembleOneInstruction(memory, instructions, address);
		else
		{
			instructions.addInstruction(address, getData(memory, address, description.getLength()));
			address += description.getLength();
		}
		return address;
	}

	public Instruction dissambleData(byte[] memory, DataDescription description)
	{
		if (description.isCode())
			return getInstruction(memory, description.getAddress());
		else
			return getData(memory, description.getAddress(), description.getLength());
	}
	
	private int disassembleOneInstruction(byte[] memory,
			InstructionList instructions, int address) {
		Instruction instruction = getInstruction(memory, address);
		instructions.addInstruction(address, instruction);
		address += instruction.getByteCount();
		return address;
	}

	public void loadSymbols(InputStream input_stream, String sourceName) {
		symbols.load(input_stream, sourceName);
	}

	public Instruction getInstruction(byte[] memory, int address) {
		CommandDefinition def = commands.getCommand(new HwByte(memory[address]));
		return 
			new Instruction(new MemoryPtr(memory, address),
							def.getCommand(), def.getOperand(), symbols);
		
	}
	
	private Instruction getData(byte[] memory, int address, int length) {
		return new Instruction(new MemoryPtr(memory, address), length);
	}

	public SymbolTable getSymbols() {
		return symbols;
	}

}
